/*
 * The contents of this web application are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this web application except in compliance with 
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the License.
 * 
 * The Original Code is owned by and the Initial Developer of the Original Code is 
 * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
 * 
 * Section 11 of the License is EXPRESSLY amended to include a provision stating 
 * that any dispute, including but not limited to disputes related to the enforcement 
 * of the License, to which Composite A/S as owner of the Original Code, as Initial 
 * Developer or in any other role, becomes a part to shall be governed by Danish law 
 * and be initiated before the Copenhagen City Court ("K�benhavns Byret")            
 */

using System;
using System.Collections.Generic;
using Composite.Core.Collections.Generic;


namespace Composite.Core.Types
{
    internal sealed class CompiledTypeCache<TValue>
    {
        private readonly Hashtable<string, CacheItem> _cache = new Hashtable<string, CacheItem>();


        public void RemoveOldVersion(Type key)
        {
            string cacheKey = GetCacheKey(key);

            CacheItem cacheItem;
            if (_cache.TryGetValue(cacheKey, out cacheItem))
            {               
                _cache.Remove(cacheKey);
            }
        }



        public void Add(Type key, TValue value)
        {
            string cacheKey = GetCacheKey(key);

            CacheItem cacheItem;
            if (_cache.TryGetValue(cacheKey, out cacheItem))
            {
                if (cacheItem.Type == key) throw new ArgumentException("Adding an item with duplicate key");

                _cache[cacheKey] = new CacheItem { Type = key, Value = value };
            }
            else
            {
                _cache.Add(cacheKey, new CacheItem { Type = key, Value = value });
            }
        }



        public bool ContainsKey(Type key)
        {
            CacheItem cacheItem;
            if (_cache.TryGetValue(GetCacheKey(key), out cacheItem) == false) return false;
           
            return true;
        }

        private static string GetCacheKey(Type key)
        {
            return TypeManager.SerializeType(key) + key.Assembly.FullName;
        }


        public bool TryGetValue(Type key, out TValue value)
        {
            CacheItem cacheItem;
            if (!_cache.TryGetValue(GetCacheKey(key), out cacheItem))
            {
                value = default(TValue);
                return false;
            }
           

            value = cacheItem.Value;            

            return true;
        }



        public IEnumerable<Type> Keys
        {
            get
            {
                foreach (CacheItem item in _cache.GetValues())
                {
                    yield return item.Type;
                }
            }
        }



        public TValue this[Type key]
        {
            get
            {
                CacheItem cacheItem;
                if (_cache.TryGetValue(GetCacheKey(key), out cacheItem) == false)
                {
                    throw new ArgumentException("Key not found");
                }                

                return cacheItem.Value;
            }
            set
            {
                string cacheKey = GetCacheKey(key);

                CacheItem cacheItem;
                if (!_cache.TryGetValue(cacheKey, out cacheItem)) throw new ArgumentException("Key not found");               

                _cache[cacheKey] = new CacheItem { Type = key, Value = value };
            }
        }



        


        private sealed class CacheItem
        {
            public Type Type { get; set; }
            public TValue Value { get; set; }
        }
    }
}
